Skip to content

Conversation

@dmaier-redislabs
Copy link
Contributor

@dmaier-redislabs dmaier-redislabs commented Dec 15, 2025

Added a local digest command to the client to execute the XXH3 locally rather than using a server roundtrip.

@petyaslavova I added this for now as a command next to the digest command. There are indeed some discussion points:

  • Should we expose this as a normal command digest_local?
  • Would it make sense to parameterize the digest command instead to have a boolean exec_local parameter?
  • Do we want to expose this that way or move it more to helper section

BTW: This adds xxhash~=3.6.0 as an additional dependency.

@jit-ci
Copy link

jit-ci bot commented Dec 15, 2025

Hi, I’m Jit, a friendly security platform designed to help developers build secure applications from day zero with an MVS (Minimal viable security) mindset.

In case there are security findings, they will be communicated to you as a comment inside the PR.

Hope you’ll enjoy using Jit.

Questions? Comments? Want to learn more? Get in touch with us.

@petyaslavova petyaslavova added the feature New feature label Jan 8, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds a new digest_local command to compute XXH3 hashes locally on the client side, eliminating the need for a server roundtrip. This is particularly useful for conditional operations like IFDEQ/IFDNE where the digest needs to be computed client-side before sending commands.

Changes:

  • Added digest_local method to compute XXH3 digests locally using the xxhash library
  • Added xxhash as an optional dependency with version constraint ~=3.6.0
  • Added comprehensive tests for both sync and async clients to verify local digest matches server digest

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
redis/commands/core.py Implements the digest_local method with xxhash library import handling and encoding/decoding logic
pyproject.toml Adds xxhash as an optional dependency under the [project.optional-dependencies] section
dev_requirements.txt Pins xxhash==3.6.0 for development environment
tests/test_commands.py Adds test to verify local digest matches server digest, and integrates digest_local into existing IFDEQ/IFDNE tests
tests/test_asyncio/test_commands.py Adds async version of the local digest test

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

local_digest = xxhash.xxh3_64(value).hexdigest()

# To align with digest, we want to return bytes if decode_responses is False.
# The following should work because Python's mixin approach.
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment "The following should work because Python's mixin approach" is unclear and grammatically incomplete. Consider revising to: "The following should work because of Python's mixin approach" or providing more context about what specifically about the mixin approach makes this work.

Suggested change
# The following should work because Python's mixin approach.
# The following works because of Python's mixin-based client class hierarchy.

Copilot uses AI. Check for mistakes.
Comment on lines +1282 to +1283
if isinstance(res_server, bytes):
assert isinstance(res_local, bytes)
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test only verifies that res_local is bytes when res_server is bytes, but doesn't verify the opposite case when decode_responses=True. Consider adding an else clause to assert that both are strings when decode_responses=True, ensuring type consistency between server and local digest in both modes.

Copilot uses AI. Check for mistakes.
Comment on lines +1820 to +1822
@pytest.mark.parametrize(
"value", [b"", b"abc", b"The quick brown fox jumps over the lazy dog"]
)
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test only covers bytes values but the digest_local method accepts Union[bytes, str] according to its type annotation. Consider adding test cases with string values to ensure the method correctly handles both bytes and str inputs, especially considering encoding issues.

Copilot uses AI. Check for mistakes.
Comment on lines +1268 to +1270
@pytest.mark.parametrize(
"value", [b"", b"abc", b"The quick brown fox jumps over the lazy dog"]
)
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test only covers bytes values but the digest_local method accepts Union[bytes, str] according to its type annotation. Consider adding test cases with string values to ensure the method correctly handles both bytes and str inputs, especially considering encoding issues.

Copilot uses AI. Check for mistakes.
Comment on lines +5 to +13

# Try to import the xxhash library as an optional dependency
try:
import xxhash

HAS_XXHASH = True
except ImportError:
HAS_XXHASH = False

Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The optional import block should be placed after other standard library imports to follow PEP 8 import ordering conventions. The blank line on line 5 creates unnecessary separation in the standard library imports section. Consider moving the try-except block to after line 14 (after the warnings import) and removing the blank line at line 5.

Copilot uses AI. Check for mistakes.
Comment on lines 1901 to 1932
def digest_local(self, value: Union[bytes, str]) -> Union[bytes, str]:
"""
Compute the hexadecimal digest of the value locally, without sending it to the server.
This is useful for conditional operations like IFDEQ/IFDNE where you need to
compute the digest client-side before sending a command.
Warning:
**Experimental** - This API may change or be removed without notice.
Arguments:
- value: Union[bytes, str] - the value to compute the digest of.
Returns:
- (str | bytes) the XXH3 digest of the value as a hex string (16 hex characters)
For more information, see https://redis.io/commands/digest
"""
if not HAS_XXHASH:
raise NotImplementedError(
"XXHASH library not installed. Install with: "
"'pip install xxhash' or 'pip install redis[xxhash]' to use this feature."
)

local_digest = xxhash.xxh3_64(value).hexdigest()

# To align with digest, we want to return bytes if decode_responses is False.
# The following should work because Python's mixin approach.
if not self.get_encoder().decode_responses:
local_digest = local_digest.encode()

return local_digest
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing test coverage for the case when the xxhash library is not installed. Consider adding a test that verifies the NotImplementedError is raised with an appropriate message when digest_local is called without the xxhash dependency available. This would ensure the error handling path works correctly.

Copilot uses AI. Check for mistakes.
Comment on lines +1832 to +1833
if isinstance(res_server, bytes):
assert isinstance(res_local, bytes)
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test only verifies that res_local is bytes when res_server is bytes, but doesn't verify the opposite case when decode_responses=True. Consider adding an else clause to assert that both are strings when decode_responses=True, ensuring type consistency between server and local digest in both modes.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants